package com.wonders.utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 功能描述:用工具类操作操作redis
 * @Author: yyalin
 * @CreateDate: 2023/8/13 16:04
 */
@Service
@Slf4j
public class RedisUtils {
    //初始化Lua脚本文件
    private static final DefaultRedisScript<Long> UNLOCK_SCRIPT;
    static {
        UNLOCK_SCRIPT = new DefaultRedisScript<>();
        //lua脚本位置
        UNLOCK_SCRIPT.setLocation(new ClassPathResource("luaFiles/unlock.lua"));
        //返回值类型
        UNLOCK_SCRIPT.setResultType(Long.class);
    }
    @Autowired
    private RedisTemplate<Object,Object> redisTemplate;
    /**
     * @class RedisUtil
     * @存入redis缓存中数据 String类型的
     * @author 杨亚林
     * @createDate 2018年6月7日
     */
    public boolean set(String key,Object value){
        boolean res=false;
        try {
            ValueOperations<Object, Object> valueOperations=redisTemplate.opsForValue();
            valueOperations.set(key, value);
            res=true;
        } catch (Exception e) {
            log.error("保存异常！"+e);
        }
        return res;
    }
    /**
     * 删除对应的value
     * @param key
     */
    public boolean remove(final String key) {
        if (exists(key)) {
            Boolean delete = redisTemplate.delete(key);
            return delete;
        }
        return false;

    }
    /**
     * 判断缓存中是否有对应的value
     * @param key
     * @return
     */
    public boolean exists(final String key) {
        return redisTemplate.hasKey(key);
    }
    /**
     * 将值 value 关联到 key ，并将 key 的过期时间设为 timeout
     *
     * @param key     键值
     * @param value   值
     * @param timeout 过期时间
     * @param unit    时间单位, 天:TimeUnit.DAYS 小时:TimeUnit.HOURS 分钟:TimeUnit.MINUTES
     *                秒:TimeUnit.SECONDS 毫秒:TimeUnit.MILLISECONDS
     */
    public void setEx(String key, Object value, long timeout, TimeUnit unit) throws Exception {
        redisTemplate.opsForValue().set(key, value, timeout, unit);
    }
    /**
     * 功能描述:自定义设置setEx 单位为：天
     * @MethodName: setExVo
     * @MethodParam: [key, value, timeout, unit]
     * @Return: void
     * @Author: yyalin
     * @CreateDate: 2022/9/7 11:06
     */
    public void setExVo(String key, Object value) throws Exception {
        log.info("redis缓存获取超时时间(天)cache_timeout:");
        redisTemplate.opsForValue().set(key, value, 30L,  TimeUnit.DAYS);
    }


    /**
     * @class RedisUtil
     * @根据key获取缓存中的数据 String类型的
     * @author 杨亚林
     * @createDate 2018年6月7日
     */
    public Object get(String key){
        Object obj=null;
        ValueOperations<Object, Object> valueOperations=redisTemplate.opsForValue();
        obj=valueOperations.get(key);
        return obj;
    }
    /**
     * @Author yangyalin
     * @Description
     * @Date 15:54 2018/11/1
     * @Param [key]
     * @return java.lang.String
     **/
    public Boolean del(String key){
        Boolean flag=true;
        try {
            log.info("开始删除key:{}",key);
            redisTemplate.delete(key);
        }catch (Exception e){
            log.error("删除{}异常",key);
            flag=false;
        }
        return flag;
    }
    /**
     * 字符串类型:根据key设置value值,如果key中的value存在,那么返回false
     * 第一种方案
     * @param key
     * @param value
     * @return
     */
    public boolean setnx(String key, Object value) {
        boolean ok=false;
        try {
            ok=redisTemplate.opsForValue().setIfAbsent(key, value);
        } catch (Exception e) {
            // TODO: handle exception
            log.error("setnx执行失败！"+e);
        }
        return ok;
    }
     /**
      * @Author yangyalin
      * @Description 第二种方案，重构setnx的方法
      * @Date 16:59 2018/11/20
      * @Param [key, value, time, timeUnit]
      * @return boolean
      **/
    public boolean setnx(String key, Object value,Long time,TimeUnit timeUnit) {
        boolean ok=false;
        try {
            redisTemplate.opsForValue().setIfAbsent(key,value,time,timeUnit);
            ok=true;
        } catch (Exception e) {
            // TODO: handle exception
            log.error("setnx执行失败！"+e);
        }
        return ok;
    }
    /**
     * <p>Title: getSet</p>
     * <p>Description:字符串：创建和获取旧值 String类型 </p>
     * @param key
     * @param value
     * @return
     */
    public Object getSet(String key,Object value){
        Object obj="error";
        try {
            obj=redisTemplate.opsForValue().getAndSet(key, value);
        } catch (Exception e) {
            // TODO: handle exception
            log.error("getSet执行失败！"+e);
        }
        return obj;
    }

    /**
     * @Author yangyalin
     * @Description 方案1：解锁
     * @Date 16:31 2018/11/9
     * @Param [lockKey, lockValue]
     * @return void
     **/
//    public void unlock(String lockKey,String lockValue){
//        try {
//            String currentValue=String.valueOf(redisTemplate.opsForValue().get(lockKey));
//            if(!StringUtils.isEmpty(currentValue) && currentValue.equals(lockValue)){
//                //redisTemplate.opsForValue().getOperations().delete(lockKey);
//                redisTemplate.delete(lockKey);
//                log.info("解锁成功！");
//            }
//        } catch (Exception e) {
//            log.info("解锁失败！{}",e);
//        }
//    }
    /**
     * 功能描述:方法二：使用Lua脚本释放锁
     * @MethodName: unlock
     * @MethodParam: [lockKey, lockValue]
     * @Return: void
     * @Author: yyalin
     * @CreateDate: 2023/8/13 16:09
     */
    public void unlock(String lockKey,String lockValue){
        // 调用lua脚本
        redisTemplate.execute(
                UNLOCK_SCRIPT,
                Collections.singletonList(lockKey),
                lockValue);
    }

    /**
     * @Author yangyalin
     * @Description 发布消息
     * @Date 11:24 2018/11/22
     * @Param [channel：消息信道, message：消息内容]
     * @return void
     **/
    public void sendMessage(String channel,String message){
        redisTemplate.convertAndSend(channel,message);
    }
    /**
     * @Author yangyalin
     * @Description 使用管道进行批量添加操作
     * @Date 10:16 2018/11/23
     * @Param []
     * @return void
     **/
    public void usePipeline(){
        //执行后的结果result Object：返回的对象
        List<Object> result=redisTemplate.executePipelined(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection redisConnection) throws DataAccessException {
                redisConnection.openPipeline();
                for (int i=0;i<1000;i++){
                    redisConnection.set(("bb"+i).getBytes(),(i+"aa").getBytes());
                }
                return null;
            }
        },redisTemplate.getValueSerializer());
    }
    /**
     * @Author yangyalin
     * @Description 使用管道进行批量删除操作
     * @Date 10:16 2018/11/23
     * @Param []
     * @return void
     **/
    public List<Object> usePipelineFind(){
        //执行后的结果result Object：返回的对象
        List<Object> result=redisTemplate.executePipelined(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection redisConnection) throws DataAccessException {
                redisConnection.openPipeline();
                for (int i=0;i<1000;i++){
                    redisConnection.del(("bb"+i).getBytes());
                }
                return null;
            }
        });
        return result;
    }



}
